﻿Imports System
Imports System.Collections.Generic
Imports System.Collections.Specialized
Imports System.IO
Imports System.Text.RegularExpressions

Namespace GoogleAnalyticsMailHandler
    Public Class Util
        Protected Shared BoundaryRegex As New Regex("Content-Type: multipart(?:/\S+;)" + "\s+[^" & vbCr & vbLf & "]*boundary=""?(?<boundary>" + "[^""" & vbCr & vbLf & "]+)""?" & vbCr & vbLf, RegexOptions.IgnoreCase Or RegexOptions.Compiled)
        Protected Shared UtcDateTimeRegex As New Regex("^(?:\w+,\s+)?(?<day>\d+)\s+(?<month>\w+)\s+(?<year>\d+)\s+(?<hour>\d{1,2})" + ":(?<minute>\d{1,2}):(?<second>\d{1,2})\s+(?<offsetsign>\-|\+)(?<offsethours>" + "\d{2,2})(?<offsetminutes>\d{2,2})(?:.*)$", RegexOptions.IgnoreCase Or RegexOptions.Compiled)
        Public Shared Function ParseHeaders(ByVal headerText As String) As NameValueCollection
            Dim headers As New NameValueCollection()
            Dim reader As New StringReader(headerText)
            Dim line As String
            Dim headerName As String = Nothing, headerValue As String
            Dim colonIndx As Integer
            While (InlineAssignHelper(line, reader.ReadLine())) IsNot Nothing
                If line = "" Then
                    Exit While
                End If
                If [Char].IsLetterOrDigit(line(0)) AndAlso (InlineAssignHelper(colonIndx, line.IndexOf(":"c))) <> -1 Then
                    headerName = line.Substring(0, colonIndx)
                    headerValue = line.Substring(colonIndx + 1).Trim()
                    headers.Add(headerName, headerValue)
                ElseIf headerName IsNot Nothing Then
                    headers(headerName) += " " + line.Trim()
                Else
                    Throw New FormatException("Could not parse headers")
                End If
            End While
            Return headers
        End Function
        Public Shared Function ParseMessageParts(ByVal emailText As String) As List(Of MessagePart)
            Dim messageParts As New List(Of MessagePart)()
            Dim newLinesIndx As Integer = emailText.IndexOf(vbCr & vbLf & vbCr & vbLf)
            Dim m As Match = BoundaryRegex.Match(emailText)
            If m.Index < emailText.IndexOf(vbCr & vbLf & vbCr & vbLf) AndAlso m.Success Then
                Dim boundary As String = m.Groups("boundary").Value
                Dim startingBoundary As String = vbCr & vbLf & "--" + boundary
                Dim startingBoundaryIndx As Integer = -1
                While True
                    If startingBoundaryIndx = -1 Then
                        startingBoundaryIndx = emailText.IndexOf(startingBoundary)
                    End If
                    If startingBoundaryIndx <> -1 Then
                        Dim nextBoundaryIndx As Integer = emailText.IndexOf(startingBoundary, startingBoundaryIndx + startingBoundary.Length)
                        If nextBoundaryIndx <> -1 AndAlso nextBoundaryIndx <> startingBoundaryIndx Then
                            Dim multipartMsg As String = emailText.Substring(startingBoundaryIndx + startingBoundary.Length, (nextBoundaryIndx - startingBoundaryIndx - startingBoundary.Length))
                            Dim headersIndx As Integer = multipartMsg.IndexOf(vbCr & vbLf & vbCr & vbLf)
                            If headersIndx = -1 Then
                                Throw New FormatException("Incompatible multipart message format")
                            End If
                            Dim bodyText As String = multipartMsg.Substring(headersIndx).Trim()
                            Dim headers As NameValueCollection = Util.ParseHeaders(multipartMsg.Trim())
                            messageParts.Add(New MessagePart(headers, bodyText))
                        Else
                            Exit While
                        End If
                        startingBoundaryIndx = nextBoundaryIndx
                    Else
                        Exit While
                    End If
                End While
                If newLinesIndx <> -1 Then
                    Dim emailBodyText As String = emailText.Substring(newLinesIndx + 1)
                End If
            Else
                Dim headersIndx As Integer = emailText.IndexOf(vbCr & vbLf & vbCr & vbLf)
                If headersIndx = -1 Then
                    Throw New FormatException("Incompatible multipart message format")
                End If
                Dim bodyText As String = emailText.Substring(headersIndx).Trim()
                Dim headers As NameValueCollection = Util.ParseHeaders(emailText)
                messageParts.Add(New MessagePart(headers, bodyText))
            End If
            Return messageParts
        End Function
        Public Shared Function ConvertStrToUtcDateTime(ByVal str As String) As DateTime
            Dim m As Match = UtcDateTimeRegex.Match(str)
            Dim day As Integer, month As Integer, year As Integer, hour As Integer, min As Integer, sec As Integer
            If m.Success Then
                day = Convert.ToInt32(m.Groups("day").Value)
                year = Convert.ToInt32(m.Groups("year").Value)
                hour = Convert.ToInt32(m.Groups("hour").Value)
                min = Convert.ToInt32(m.Groups("minute").Value)
                sec = Convert.ToInt32(m.Groups("second").Value)
                Select Case m.Groups("month").Value
                    Case "Jan"
                        month = 1
                        Exit Select
                    Case "Feb"
                        month = 2
                        Exit Select
                    Case "Mar"
                        month = 3
                        Exit Select
                    Case "Apr"
                        month = 4
                        Exit Select
                    Case "May"
                        month = 5
                        Exit Select
                    Case "Jun"
                        month = 6
                        Exit Select
                    Case "Jul"
                        month = 7
                        Exit Select
                    Case "Aug"
                        month = 8
                        Exit Select
                    Case "Sep"
                        month = 9
                        Exit Select
                    Case "Oct"
                        month = 10
                        Exit Select
                    Case "Nov"
                        month = 11
                        Exit Select
                    Case "Dec"
                        month = 12
                        Exit Select
                    Case Else
                        Throw New FormatException("Unknown month.")
                End Select
                Dim offsetSign As String = m.Groups("offsetsign").Value
                Dim offsetHours As Integer = Convert.ToInt32(m.Groups("offsethours").Value)
                Dim offsetMinutes As Integer = Convert.ToInt32(m.Groups("offsetminutes").Value)
                Dim dt As New DateTime(year, month, day, hour, min, sec)
                If offsetSign = "+" Then
                    dt.AddHours(offsetHours)
                    dt.AddMinutes(offsetMinutes)
                ElseIf offsetSign = "-" Then
                    dt.AddHours(-offsetHours)
                    dt.AddMinutes(-offsetMinutes)
                End If
                Return dt
            End If
            Throw New FormatException("Incompatible date/time string format")
        End Function
        Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
            target = value
            Return value
        End Function
    End Class
End Namespace
